﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Web.Mvc;
using System.Web.Routing;

using mvcForum.Core;
using mvcForum.Core.Abstractions.Interfaces;
using mvcForum.Web.Controllers;
using mvcForum.Web.Extensions;
using mvcForum.Web.ViewModels;
using mvcForum.Core.Specifications;
using mvcForum.Core.Interfaces.Data;
using mvcForum.Web.Interfaces;

namespace mvcForum.Web.Areas.Forum.Controllers {

	/// <summary>
	/// Home controller.
	/// The controller handles showing all the categories and their forums.
	/// TODO: How do we select the board? if more than one in the solution?
	/// </summary>
	public class HomeController : ThemedForumBaseController {
		private readonly IConfiguration config;
		private readonly IBoardRepository boardRepo;
		private readonly IForumRepository forumRepo;
		private readonly IUserService userService;

		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="config">Injected IConfiguration instance.</param>
		/// <param name="userProvider">Injected IWebUserProvider instance.</param>
		public HomeController(IConfiguration config,
								IWebUserProvider userProvider,
								IContext context,
								IBoardRepository boardRepo,
								IForumRepository forumRepo,
								IUserService userService)
			: base(userProvider, context) {

			// Store the configuration instance as a variable on the controller instance.
			this.config = config;
			this.boardRepo = boardRepo;
			this.forumRepo = forumRepo;
			this.userService = userService;
		}

		#region Read
		public ActionResult Index() {
			// TODO: Get from config!!
			Board board = null;
			String boardIdString = String.Empty;
			Int32 boardId;
			if (!String.IsNullOrWhiteSpace(boardIdString) && Int32.TryParse(boardIdString, out boardId)) {
				board = this.boardRepo.ReadOneOptimized(new BoardSpecifications.ById(boardId));
			}

			if (board == null) {
				IEnumerable<Board> allEnabledBoards = this.boardRepo.ReadManyOptimized(new BoardSpecifications.Enabled());
				if (!allEnabledBoards.Any()) {
					return RedirectToAction("index", "basicinstall", new { area = "forumadmin" });
				}
				// Get the first board in the returned list.
				board = allEnabledBoards.First();
			}

			// Create the board model.
			BoardViewModel gvm = new BoardViewModel(board);
			// We're at the root, so the breadcrumb path will be empty.
			gvm.Path = new Dictionary<String, String>();
			gvm.ShowOnline = this.config.ShowOnlineUsers;
			if (gvm.ShowOnline) {
				IEnumerable<ForumUser> user = this.userService.GetOnlineUsers();
				gvm.OnlineUsers = user.OrderBy(u => u.AuthorName(this.config));
			}

			// Container for the category models.
			List<CategoryViewModel> categoryVMs = new List<CategoryViewModel>();
			// Get all the categories of the board, sort by the SortOrder property.
			foreach (Category category in board.Categories.OrderBy(c => c.SortOrder)) {
				// Create the category model.
				CategoryViewModel categoryVM = new CategoryViewModel(category);
				// Add the model.
				categoryVMs.Add(categoryVM);

				IEnumerable<Core.Forum> subForums = this.forumRepo.ReadManyOptimized(new ForumSpecifications.SpecificCategoryNoParentForum(category));
				// Get the forums of the category, sort by the SortOrder property.
				categoryVM.Forums = subForums.OrderBy(f => f.SortOrder).Select(f => new ForumViewModel(f, this.config.TopicsPerPage, this.config.AuthorNameSetting) { SubForums = f.SubForums.Select(sf => new ForumViewModel(sf, this.config.TopicsPerPage, this.config.AuthorNameSetting)) });
			}
			gvm.Categories = new ReadOnlyCollection<CategoryViewModel>(categoryVMs);

			return View(gvm);
		}
		#endregion

		#region Breadcrumb/Path
		// TODO: Move to ForumHelper
		[NonAction]
		public static void BuildPath(Core.Forum forum, Dictionary<String, String> path, UrlHelper urlHelper) {
			if (forum.ParentForum != null) {
				BuildPath(forum.ParentForum, path, urlHelper);
			}
			else {
				BuildPath(forum.Category, path, urlHelper);
			}
			string url = urlHelper.RouteUrl("ShowForum", new RouteValueDictionary { { "id", forum.Id }, { "title", forum.Name.ToSlug() } });
			path.Add(url, forum.Name);
		}

		[NonAction]
		public static void BuildPath(Category category, Dictionary<String, String> path, UrlHelper urlHelper) {
			string url = urlHelper.RouteUrl("ShowCategory", new RouteValueDictionary { { "id", category.Id }, { "title", category.Name.ToSlug() } });
			path.Add(url, category.Name);
		}

		[NonAction]
		public static void BuildPath(Topic topic, Dictionary<String, String> path, UrlHelper urlHelper) {
			BuildPath(topic.Forum, path, urlHelper);
			string url = urlHelper.RouteUrl("ShowTopic", new RouteValueDictionary { { "id", topic.Id }, { "title", topic.Title.ToSlug() } });
			path.Add(url, topic.Title);
		}
		#endregion

		/// <summary>
		/// The method marks all forums of the given board as read.
		/// </summary>
		/// <param name="id">Id of the board.</param>
		/// <returns></returns>
		[Authorize]
		[ActionName("Mark As Read")]
		public ActionResult MarkAsRead(Int32 id) {
			IRepository<Board> boardRepository = this.GetRepository<Board>();
			// Get the board!
			var board = boardRepository.Read(id);
			// Get all categories on the board.
			foreach (var cat in board.Categories) {
				// Get the forums for the category
				// Let's iterate them!
				foreach (var forum in cat.Forums) {
					// Does the user have access to this forum?
					if (forum.HasAccess(AccessFlag.Read)) {
						// Yes, so let's update the "last viewed" data!
						forum.Track();
					}
				}
			}

			// Done, back to the board view!
			return RedirectToAction("index");
		}
	}
}